//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================


#define _NO_INCLIST
#include <eltypes.h>
#include <elasys_server.h>
_ELASTOS_NAMESPACE_USING

#include <eladef.h>
#include <stdlib.h>
#include <string.h>
#include <_pubcrt.h>
#include <bootmods.h>

static char *s_pszProFile = NULL;
static UInt32 s_uSize = 0;

inline  ECode ValueToReturn(const char *s, AStringBuf *pReturnedString)
{
    Int32 i = 0, j = pReturnedString->GetCapacity();
    char *p = (char *)_alloca(j);

    for (i = 0; i < j && s[i] && s[i] != '\r' && s[i] != '\n'; i++) {
        p[i] = s[i];
    }

    p[i] = '\0';

    pReturnedString->Copy(p);

    return NOERROR;
}

inline const char * SkipSpace(const char *s)
{
    while (*s == ' ' || *s == '\t') s++;
    return s;
}

inline const char * StringCompare(const char* Name, const char* Str, char StopChar)
{
    Str = SkipSpace(Str);   //Skip lead Space

    char c1, c2;
    do {
        c1 = *Str++;
        if (c1 >= 'A' && c1 <= 'Z')     // convert to lower case.
            c1 -= 'A' - 'a';

        c2 = (char)*Name++;
        if (c2 >= 'A' && c2 <= 'Z')     // convert to lower case.
            c2 -= 'A' - 'a';
    } while(c1 && (c1 != StopChar) && c2 && (c1 == c2));

    Str--;
    Str = SkipSpace(Str);   //Skip lead Space

    if ((StopChar == *Str) && (0 == c2))
        return Str;
    else
        return NULL;
}

inline int _atoh(char *str)
{
    int result = 0;
    int base = 10;
    int sign = 1;

    for(;' ' == *str || '\t' == *str; str++);

    if (*str == '-') {
        sign = -1;
        str++;
    }
    else if (str[0] == '0' && (str[1] == 'x')) {
        base = 16;
        str += 2;//Skip
    }

    int c;
    while((c = *str++)) {
        if (c >= '0' && c <= '9') {
            c -= '0';
        }
        else if (base == 16) {
            if (c >= 'a' && c <= 'f') {
                c -= 'a' - 10;
            }
            else if (c >= 'A' && c <= 'F') {
                c -= 'A' - 10;
            }
            else break;
        }
        else break;

        result *= base;
        result += c;
    }

    return result * sign;
}

inline const char * NextLine(const char *pString, const char *pEnd)
{
    while (pString <= pEnd) {
        if (*pString == '\r' || *pString == '\n') {
            //delete the blank line
            do {
                pString++;
            }while(*pString == '\r' || *pString == '\n');
            return pString;
        }
        pString++;
    }

    return NULL;
}

inline ECode GetProfileStringFromBuffer(AString appName, AString keyName,
                AStringBuf *pReturnedString, char *pFileBuf, UInt32 uLength)
{

    const char *pString = pFileBuf, *pValue = NULL;

    //STEP 1. Find Section
    do {
        if (*pString == ';' || *pString == '#') continue; //skip comment

        if (*pString++ == '[') {//Section Begin
            if (StringCompare(appName, pString, ']')) break;//GOT
        }
    }while(pString = NextLine(pString, pFileBuf + uLength));

    if (!pString) {
        return E_DOES_NOT_EXIST;
    }

    //STEP 2. Find Field
    while(pString = NextLine(pString, pFileBuf + uLength)) {
        if (*pString == ';' || *pString == '#') continue; //skip comment

        //
        // end of this section, begin the next section
        //
        if (*pString == '[') break;

        pValue = StringCompare(keyName, pString, '=');
        if (pValue) {//GOT IT
            pValue = SkipSpace(pValue + 1);
            if (!pValue) {
                return E_DOES_NOT_EXIST;
            }

            return ValueToReturn(pValue, pReturnedString);
        }
    }

    return E_DOES_NOT_EXIST;
}

ELAPI _CProfile_GetAString(
    /* [in] */ AString appName,
    /* [in] */ AString keyName,
    /* [out] */ AStringBuf *pReturnedString)
{
    if (appName.IsNullOrEmpty() || keyName.IsNullOrEmpty()
        || !pReturnedString || !pReturnedString->GetCapacity()) {
        return E_INVALID_ARGUMENT;
    }

    ECode ec = NOERROR;
    if (!s_pszProFile) {
        ec = CrtGetBootModule(L"elastos.cfg", (UInt32 *)&s_pszProFile, &s_uSize);
        if FAILED(ec) {
            s_pszProFile = NULL;
            return ec;
        }
        assert(s_pszProFile);
        assert(s_uSize);
    }

    return GetProfileStringFromBuffer(appName, keyName, pReturnedString,
            s_pszProFile, s_uSize);
}

ELAPI _CProfile_GetWString(WString appName, WString keyName, WStringBuf *pReturnedString);

ECode _CProfile_GetWString(
        /* [in] */ WString appName,
        /* [in] */ WString keyName,
        /* [out] */ WStringBuf *pReturnedString)
{
    if (appName.IsNullOrEmpty() || keyName.IsNullOrEmpty()
        || !pReturnedString || !pReturnedString->GetCapacity()) {
        return E_INVALID_ARGUMENT;
    }

    AStringBuf_<_MAX_PATH> asAppName;
    AStringBuf_<_MAX_PATH> asKeyName;
    AStringBuf_<_MAX_PATH> asReturnedString;

    asAppName.Copy(appName);
    asKeyName.Copy(keyName);

    ECode ec = _CProfile_GetAString(asAppName, asKeyName, &asReturnedString);

    if (ec == NOERROR) pReturnedString->Copy(asReturnedString);

    return ec;
}

ELAPI _CProfile_GetInt32(AString appName, AString keyName, Int32 *pValue);

ECode _CProfile_GetInt32(
    /* [in] */ AString appName,
    /* [in] */ AString keyName,
    /* [out] */ Int32 *pValue)
{
    if (appName.IsNullOrEmpty() || keyName.IsNullOrEmpty() || !pValue) {
        return E_INVALID_ARGUMENT;
    }

    AStringBuf_<32> asInt;
    ECode ec = _CProfile_GetAString(appName, keyName, &asInt);
    if (ec == NOERROR) {
        *pValue = _atoh((char *)asInt);
    }

    return ec;
}

ELAPI _CPrivateProfile_GetAString(AString fileName, AString appName,
                                  AString keyName, AStringBuf *pReturnedString);

ECode _CPrivateProfile_GetAString(
        /* [in] */ AString fileName,
        /* [in] */ AString appName,
        /* [in] */ AString keyName,
        /* [out] */ AStringBuf *pReturnedString)
{
    if (fileName.IsNullOrEmpty() || appName.IsNullOrEmpty()
        || keyName.IsNullOrEmpty() || !pReturnedString
        || !pReturnedString->GetCapacity()) {
        return E_INVALID_ARGUMENT;
    }

    int len = (strlen(fileName) + 1) * sizeof(wchar_t);
    wchar_t *pwFileName = (wchar_t *)_alloca(len);
    if (!pwFileName) return E_OUT_OF_MEMORY;
    _strtowcs(pwFileName, fileName);

    IDiskManager *pDiskMgr = NULL;
    IBasicDirectory* pDir = NULL;
    IBasicFile *pFile = NULL;
    IAbsoluteIO*  pAbIO = NULL;

    Int32 length = 0;

    WStringBuf_<_MAX_FNAME> ewsbFile;
    ECode ec = CrtFindService(L"diskmgr", (IInterface **)&pDiskMgr);
    if (FAILED(ec)) return ec;

    ec = pDiskMgr->AcquireDirInterface(pwFileName, &pDir, &ewsbFile);
    pDiskMgr->Release();
    if (FAILED(ec)) return ec;


    ec = pDir->Lookup(ewsbFile, &pFile);
    pDir->Release();
    if (FAILED(ec)) return ec;

    pAbIO = IAbsoluteIO::Probe(pFile); if (pAbIO) pAbIO->AddRef();
    pFile->Release();
    if (!pAbIO) return E_NO_INTERFACE;

    ec = pAbIO->GetSize(&length);
    if (FAILED(ec)) {
        pAbIO->Release();
        return ec;
    }

    char *pFileBuf = (char *)malloc(length + 1);
    if (!pFileBuf) {
        pAbIO->Release();
        return E_OUT_OF_MEMORY;
    }

    MemoryBuf buf((Byte *)pFileBuf, length + 1, length);
    ec = pAbIO->Read(0, length, &buf);
    if (FAILED(ec)) {
        goto Exit;
    }

    pFileBuf[length] = '\0';

    ec = GetProfileStringFromBuffer(appName, keyName,
            pReturnedString, pFileBuf, length);

    Exit:
    if (pAbIO) pAbIO->Release();
    if (pFileBuf) free(pFileBuf);

    return ec;
}

ELAPI _CPrivateProfile_GetWString(WString fileName, WString appName,
                                  WString keyName, WStringBuf *pReturnedString);

ECode _CPrivateProfile_GetWString(
        /* [in] */ WString fileName,
        /* [in] */ WString appName,
        /* [in] */ WString keyName,
        /* [out] */ WStringBuf *pReturnedString)
{
    if (fileName.IsNullOrEmpty() || appName.IsNullOrEmpty()
        || keyName.IsNullOrEmpty() || !pReturnedString
        || !pReturnedString->GetCapacity()) {
        return E_INVALID_ARGUMENT;
    }

    AStringBuf_<_MAX_PATH> asFileName;
    AStringBuf_<_MAX_PATH> asAppName;
    AStringBuf_<_MAX_PATH> asKeyName;
    AStringBuf_<_MAX_PATH> asReturnedString;

    asFileName.Copy(fileName);
    asAppName.Copy(appName);
    asKeyName.Copy(keyName);

    ECode ec = _CPrivateProfile_GetAString(asFileName, asAppName, asKeyName,
                &asReturnedString);

    pReturnedString->Copy(asReturnedString);

    return ec;
}

ELAPI _CPrivateProfile_GetInt32(AString fileName, AString appName,
        AString keyName, Int32 *pValue);

ECode _CPrivateProfile_GetInt32(
    /* [in] */ AString fileName,
    /* [in] */ AString appName,
    /* [in] */ AString keyName,
    /* [out] */ Int32 *pValue)
{
    if (fileName.IsNullOrEmpty() || appName.IsNullOrEmpty()
        || keyName.IsNullOrEmpty() || !pValue ) {
        return E_INVALID_ARGUMENT;
    }

    AStringBuf_<32> asInt;
    ECode ec = _CPrivateProfile_GetAString(fileName, appName, keyName, &asInt);
    if (ec == NOERROR) {
        *pValue = _atoh((char *)asInt);
    }

    return ec;
}

ELAPI _CPrivateProfile_SetAString(
    /* [in] */ AString fileName,
    /* [in] */ AString appName,
    /* [in] */ AString keyName,
    /* [in] */ AString value)
{
    if (fileName.IsNullOrEmpty() || appName.IsNullOrEmpty()
        || keyName.IsNullOrEmpty() || value.IsNull()) {
        return E_INVALID_ARGUMENT;
    }

    const char *pszAppName = appName;
    const char *pszKeyName = keyName;
    const char *pszValue = value;

    int len = (strlen(fileName) + 1) * sizeof(wchar_t);
    wchar_t *pwFileName = (wchar_t *)_alloca(len);
    if (!pwFileName) return E_OUT_OF_MEMORY;
    _strtowcs(pwFileName, fileName);

    IDiskManager *pDiskMgr = NULL;
    IBasicDirectory* pDir = NULL;
    IBasicFile *pFile = NULL;
    IAbsoluteIO*  pAbIO = NULL;

    MemoryBuf *pFileBuf = NULL, *pEndBuf = NULL;
    Int32 size = 0;
    Boolean bAppNameExist = FALSE;
    Int32 beginPos = 0, endPos = 0;

    ECode ec = CrtFindService(L"diskmgr", (IInterface **)&pDiskMgr);
    if (FAILED(ec)) return ec;
    WStringBuf_<_MAX_FNAME> ewsbFile;
    ec = pDiskMgr->AcquireDirInterface(pwFileName, &pDir, &ewsbFile);
    pDiskMgr->Release();
    if (FAILED(ec)) return ec;

    if (ewsbFile.GetCapacity() == 0) {
        pDir->Release();
        return E_INVALID_ARGUMENT;
    }

    ec = pDir->Lookup(ewsbFile, &pFile);
    if (FAILED(ec)) {
        //Need to create the file
        ec = pDir->CreateFile(ewsbFile, FileAccessMode_ReadWrite, FALSE, &pFile);
        pDir->Release();
        if (FAILED(ec)) return ec;
        pAbIO = IAbsoluteIO::Probe(pFile); if (pAbIO) pAbIO->AddRef();
        pFile->Release();
        if (!pAbIO) return E_NO_INTERFACE;
    }
    else {
        pDir->Release();
        ec = pFile->CheckAccessMode(FileAccessMode_ReadWrite);
        if (FAILED(ec)) {
            pFile->Release();
            return ec;
        }

        pAbIO = IAbsoluteIO::Probe(pFile); if (pAbIO) pAbIO->AddRef();
        pFile->Release();
        if (!pAbIO) return E_NO_INTERFACE;;

        ec = pAbIO->GetSize(&size);
        if (FAILED(ec)) {
            pAbIO->Release();
            return ec;
        }

        char *pFileBuf = (char *)malloc(size + 1);
        if (!pFileBuf) {
            pAbIO->Release();
            return E_OUT_OF_MEMORY;
        }

        MemoryBuf buf((Byte *)pFileBuf, size + 1, size);
        ec = pAbIO->Read(0, size, &buf);
        if (FAILED(ec)) {
            free(pFileBuf);
            goto Exit;
        }

        pFileBuf[size] = '\0';
        const char *pString = pFileBuf;
        const char *pEnd = pString + size;

        //Find Section
        do {
            if (*pString == ';' || *pString == '#') continue; //skip comment
            if (*pString++ == '[') {//Section Begin
                if (StringCompare(pszAppName, pString, ']')) break;//GOT IT
            }
        }while(pString = NextLine(pString, pEnd));

        const char *pPreValue = NULL;
        if (pString) {
            bAppNameExist = TRUE;
            //to find keyname
            while(pString = NextLine(pString, pEnd)) {
                if (*pString == ';' || *pString == '#') continue; //skip comment

                if (*pString == '[') break;

                pPreValue = StringCompare(pszKeyName, pString, '=');
                if (pPreValue) break;
            }
        }

        if (!pString) pString = pFileBuf + size;
        do {
            pString--;
        }while(*pString == '\r' || *pString == '\n');
        pString++;

        if (!pPreValue) { // can't find the keyname
            endPos = pString - pFileBuf;
        }
        else {        // find the keyname
            //skip the line
            do {
                pPreValue++;
            }while(pPreValue < pEnd && *pPreValue != '\r' && *pPreValue != '\n');
            endPos = pPreValue - pFileBuf;
       }

        //Save end part to the pEndBuf
        if (endPos < size) {
            pEndBuf = MemoryBuf::Alloc(size - endPos);
            if (!pEndBuf) {
                free(pFileBuf);
                ec = E_OUT_OF_MEMORY;
                goto Exit;
            }
            pEndBuf->Copy((Byte *)pFileBuf + endPos, size - endPos);
        }
        free(pFileBuf);
        beginPos = pString - pFileBuf;
    }

    pFileBuf = MemoryBuf::Alloc(strlen(pszAppName) + strlen(pszKeyName)
                                         + strlen(pszValue) + 16);
    if (!pFileBuf) {
        ec = E_OUT_OF_MEMORY;
        goto Exit;
    }

    if (bAppNameExist) {
        pFileBuf->SetUsed(0);
    }
    else {
        if (beginPos) {
            pFileBuf->Copy((Byte *)"\r\n\r\n", 4);
        }
        pFileBuf->Append((Byte *)"[", 1);
        pFileBuf->Append((Byte *)pszAppName, strlen(pszAppName));
        pFileBuf->Append((Byte *)"]", 1);
    }
    pFileBuf->Append((Byte *)"\r\n", 2);
    pFileBuf->Append((Byte *)pszKeyName, strlen(pszKeyName));
    pFileBuf->Append((Byte *)" = ", 3);
    pFileBuf->Append((Byte *)pszValue, strlen(pszValue));
    if (!pEndBuf && !beginPos) {
        pFileBuf->Append((Byte *)"\r\n", 2);
    }

    ec = pAbIO->Write(beginPos, *pFileBuf, &size);
    if (FAILED(ec))  goto Exit;

    ec = pAbIO->SetSize(beginPos + size);
    if (FAILED(ec)) goto Exit;

    if (pEndBuf) {
        ec = pAbIO->Write(beginPos + size, *pEndBuf, &size);
        if (FAILED(ec)) {
            goto Exit;
        }
    }

Exit:
    if (pAbIO) pAbIO->Release();
    if (pFileBuf) MemoryBuf::Free(pFileBuf);
    if (pEndBuf) MemoryBuf::Free(pEndBuf);
    return ec;
}

ELAPI _CPrivateProfile_SetWString(WString fileName, WString appName,
        WString keyName, WString value);

ECode _CPrivateProfile_SetWString(
    /* [in] */ WString fileName,
    /* [in] */ WString appName,
    /* [in] */ WString keyName,
    /* [in] */ WString value)
{
    if (fileName.IsNullOrEmpty() || appName.IsNullOrEmpty()
        || keyName.IsNullOrEmpty() || value.IsNull()) {
        return E_INVALID_ARGUMENT;
    }

    AStringBuf_<_MAX_PATH> asFileName;
    AStringBuf_<_MAX_PATH> asAppName;
    AStringBuf_<_MAX_PATH> asKeyName;
    AStringBuf_<_MAX_PATH> asValue;

    asFileName.Copy(fileName);
    asAppName.Copy(appName);
    asKeyName.Copy(keyName);
    asValue.Copy(value);

    return _CPrivateProfile_SetAString(asFileName, asAppName, asKeyName, asValue);
}



